<?php

declare(strict_types=1);
/**
 * This file is part of Hyperf.
 *
 * @link     https://www.hyperf.io
 * @document https://hyperf.wiki
 * @contact  group@hyperf.io
 * @license  https://github.com/hyperf/hyperf/blob/master/LICENSE
 */

namespace App\Exception\Handler;

use App\Constants\ErrorCode;
use App\Middleware\EncryptResponseMiddleware;
use App\Utils\EncryptUtil;
use Hyperf\Contract\StdoutLoggerInterface;
use Hyperf\Database\Model\ModelNotFoundException;
use Hyperf\ExceptionHandler\ExceptionHandler;
use Hyperf\HttpMessage\Stream\SwooleStream;
use Hyperf\HttpServer\Router\Dispatched;
use Psr\Http\Message\MessageInterface;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Throwable;

class AppExceptionHandler extends ExceptionHandler
{
    public function __construct(protected StdoutLoggerInterface $logger)
    {
    }

    public function handle(Throwable $throwable, ResponseInterface $response): MessageInterface|ResponseInterface
    {
//        $this->logger->error(sprintf('%s[%s] in %s', $throwable->getMessage(), $throwable->getLine(), $throwable->getFile()));
//        $this->logger->error($throwable->getTraceAsString());

        $throwMsg = $throwable->getMessage() . '-' . $throwable->getFile() . '-' . $throwable->getLine();

        $returnData = [
            'code' => ErrorCode::SERVER_ERROR,
            'msg'  => $throwMsg,
            'data' => null
        ];

        if ($throwable instanceof ModelNotFoundException) {
            $model             = explode('\\', $throwable->getModel());
            $returnData['msg'] = end($model) . " Not Found";
            return $this->returnResponse($returnData, $response);
        }

        return $this->returnResponse($returnData, $response);
    }

    protected function returnResponse($returnData, ResponseInterface $response): MessageInterface|ResponseInterface
    {
        // 是否需要加密响应
        $shouldEncryptResponse = $this->routeHasEncryptResponseMiddleware(request());
        if ($shouldEncryptResponse) {

            // 加密响应数据
            $encryptedData = EncryptUtil::encrypt($returnData, get_device_identifier());

            // 阻止异常冒泡
            $this->stopPropagation();
            // 重新设置响应内容
            return $response->withAddedHeader('content-type', 'text/html')
                ->withStatus(200)->withBody(new SwooleStream($encryptedData));
        }

        // 阻止异常冒泡
        $this->stopPropagation();
        // 设置响应格式为 json
        return $response->withAddedHeader('content-type', 'application/json; charset=utf-8')
            ->withStatus(200)->withBody(new SwooleStream(
                json_encode($returnData, JSON_UNESCAPED_UNICODE)));
    }

    /**
     * 检查当前路由是否配置了加密响应中间件
     *
     * @param ServerRequestInterface $request 请求对象
     * @return bool 是否配置了加密响应中间件
     */
    protected function routeHasEncryptResponseMiddleware(ServerRequestInterface $request): bool
    {
        // 非正式环境不加密
        if (!is_prod()) {
            return false;
        }

        // 获取当前路由的 Dispatched 对象
        $dispatched = $request->getAttribute(Dispatched::class);
        if (!$dispatched || !$dispatched->isFound()) {
            return false;
        }

        // 获取路由对象
        $route = $dispatched->handler;
        if (!$route) {
            return false;
        }

        return in_array(EncryptResponseMiddleware::class, $route->options['middleware']);
    }

    public function isValid(Throwable $throwable): bool
    {
        return true;
    }
}
